#ifndef __CASE_TPL_MATH_H__
#define __CASE_TPL_MATH_H__

#include <iostream>
#include <gtest/gtest.h>
#include "basic/math.h"

typedef basic::tpl_T4<float> dkf4;
typedef basic::tpl_T4<int> dki4;

TEST(Test_dk_math, constructor) {
  dkf4 f4a;
  ASSERT_EQ(f4a.x, 0.0f);
  ASSERT_EQ(f4a.y, 0.0f);
  ASSERT_EQ(f4a.z, 0.0f);
  ASSERT_EQ(f4a.w, 0.0f);

  dkf4 f4a1(11.11f);
  ASSERT_EQ(f4a1.x, 11.11f);
  ASSERT_EQ(f4a1.y, 11.11f);
  ASSERT_EQ(f4a1.z, 11.11f);
  ASSERT_EQ(f4a1.w, 11.11f);

  dkf4 f4b(1.0f, 2.0f, 3.0f, 4.0f);
  ASSERT_EQ(f4b.x, 1.0f);
  ASSERT_EQ(f4b.y, 2.0f);
  ASSERT_EQ(f4b.z, 3.0f);
  ASSERT_EQ(f4b.w, 4.0f);

  dkf4 f4c(f4b);
  ASSERT_EQ(f4c.x, 1.0f);
  ASSERT_EQ(f4c.y, 2.0f);
  ASSERT_EQ(f4c.z, 3.0f);
  ASSERT_EQ(f4c.w, 4.0f);

  ////
  dki4 i4a;
  ASSERT_EQ(i4a.x, 0);
  ASSERT_EQ(i4a.y, 0);
  ASSERT_EQ(i4a.z, 0);
  ASSERT_EQ(i4a.w, 0);

  dki4 i4a1(1111);
  ASSERT_EQ(i4a1.x, 1111);
  ASSERT_EQ(i4a1.y, 1111);
  ASSERT_EQ(i4a1.z, 1111);
  ASSERT_EQ(i4a1.w, 1111);

  dki4 i4b(1, 2, 3, 4);
  ASSERT_EQ(i4b.x, 1);
  ASSERT_EQ(i4b.y, 2);
  ASSERT_EQ(i4b.z, 3);
  ASSERT_EQ(i4b.w, 4);

  dki4 i4c(i4b);
  ASSERT_EQ(i4c.x, 1);
  ASSERT_EQ(i4c.y, 2);
  ASSERT_EQ(i4c.z, 3);
  ASSERT_EQ(i4c.w, 4);
}

TEST(Test_dk_math, assign) {
  dkf4 f4a(1.0f, 2.0f, 3.0f, 4.0f);
  dkf4 f4b = f4a;
  ASSERT_EQ(f4b.x, 1.0f);
  ASSERT_EQ(f4b.y, 2.0f);
  ASSERT_EQ(f4b.z, 3.0f);
  ASSERT_EQ(f4b.w, 4.0f);

  dki4 i4a(1, 2, 3, 4);
  dki4 i4b = i4a;
  ASSERT_EQ(i4b.x, 1);
  ASSERT_EQ(i4b.y, 2);
  ASSERT_EQ(i4b.z, 3);
  ASSERT_EQ(i4b.w, 4);
}

TEST(Test_dk_math, plus) {
  dkf4 f4a(1.0f, 2.0f, 3.0f, 4.0f);
  dkf4 f4b(5.0f, 6.0f, 7.0f, 8.0f);
  dkf4 f4c = f4a + f4b;
  ASSERT_EQ(f4c.x, 6.0f);
  ASSERT_EQ(f4c.y, 8.0f);
  ASSERT_EQ(f4c.z, 10.0f);
  ASSERT_EQ(f4c.w, 12.0f);

  dkf4 f4c1 = f4a + 11.11f;
  ASSERT_EQ(f4c1.x, 12.11f);
  ASSERT_EQ(f4c1.y, 13.11f);
  ASSERT_EQ(f4c1.z, 14.11f);
  ASSERT_EQ(f4c1.w, 15.11f);

  f4a += f4b;
  ASSERT_EQ(f4a.x, 6.0f);
  ASSERT_EQ(f4a.y, 8.0f);
  ASSERT_EQ(f4a.z, 10.0f);
  ASSERT_EQ(f4a.w, 12.0f);

  f4a += 20.f;
  ASSERT_EQ(f4a.x, 26.0f);
  ASSERT_EQ(f4a.y, 28.0f);
  ASSERT_EQ(f4a.z, 30.0f);
  ASSERT_EQ(f4a.w, 32.0f);

  dki4 i4a(1, 2, 3, 4);
  dki4 i4b(5, 6, 7, 8);
  dki4 i4c = i4a + i4b;
  ASSERT_EQ(i4c.x, 6);
  ASSERT_EQ(i4c.y, 8);
  ASSERT_EQ(i4c.z, 10);
  ASSERT_EQ(i4c.w, 12);

  dki4 i4c1 = i4a + 1111;
  ASSERT_EQ(i4c1.x, 1112);
  ASSERT_EQ(i4c1.y, 1113);
  ASSERT_EQ(i4c1.z, 1114);
  ASSERT_EQ(i4c1.w, 1115);

  i4a += i4b;
  ASSERT_EQ(i4a.x, 6);
  ASSERT_EQ(i4a.y, 8);
  ASSERT_EQ(i4a.z, 10);
  ASSERT_EQ(i4a.w, 12);

  i4a += 20;
  ASSERT_EQ(i4a.x, 26);
  ASSERT_EQ(i4a.y, 28);
  ASSERT_EQ(i4a.z, 30);
  ASSERT_EQ(i4a.w, 32);
}

TEST(Test_dk_math, minus) {
  dkf4 f4a(1.0f, 2.0f, 3.0f, 4.0f);
  dkf4 f4b(5.0f, 6.0f, 7.0f, 8.0f);
  dkf4 f4c = f4a - f4b;
  ASSERT_EQ(f4c.x, -4.0f);
  ASSERT_EQ(f4c.y, -4.0f);
  ASSERT_EQ(f4c.z, -4.0f);
  ASSERT_EQ(f4c.w, -4.0f);

  dkf4 f4c1 = f4a - 11.11f;
  ASSERT_EQ(f4c1.x, 1.f - 11.11f);
  ASSERT_EQ(f4c1.y, 2.f - 11.11f);
  ASSERT_EQ(f4c1.z, 3.f - 11.11f);
  ASSERT_EQ(f4c1.w, 4.f - 11.11f);

  f4a -= f4b;
  ASSERT_EQ(f4a.x, -4.0f);
  ASSERT_EQ(f4a.y, -4.0f);
  ASSERT_EQ(f4a.z, -4.0f);
  ASSERT_EQ(f4a.w, -4.0f);

  f4a -= 20.f;
  ASSERT_EQ(f4a.x, -24.0f);
  ASSERT_EQ(f4a.y, -24.0f);
  ASSERT_EQ(f4a.z, -24.0f);
  ASSERT_EQ(f4a.w, -24.0f);

  dki4 i4a(1, 2, 3, 4);
  dki4 i4b(5, 6, 7, 8);
  dki4 i4c = i4a - i4b;
  ASSERT_EQ(i4c.x, -4);
  ASSERT_EQ(i4c.y, -4);
  ASSERT_EQ(i4c.z, -4);
  ASSERT_EQ(i4c.w, -4);

  dki4 i4c1 = i4a - 1111;
  ASSERT_EQ(i4c1.x, -1110);
  ASSERT_EQ(i4c1.y, -1109);
  ASSERT_EQ(i4c1.z, -1108);
  ASSERT_EQ(i4c1.w, -1107);

  i4a -= i4b;
  ASSERT_EQ(i4a.x, -4);
  ASSERT_EQ(i4a.y, -4);
  ASSERT_EQ(i4a.z, -4);
  ASSERT_EQ(i4a.w, -4);

  i4a -= 20;
  ASSERT_EQ(i4a.x, -24);
  ASSERT_EQ(i4a.y, -24);
  ASSERT_EQ(i4a.z, -24);
  ASSERT_EQ(i4a.w, -24);
}

TEST(Test_dk_math, multiply) {
  dkf4 f4a(1.0f, 2.0f, 3.0f, 4.0f);
  dkf4 f4b(5.0f, 6.0f, 7.0f, 8.0f);
  dkf4 f4c = f4a * f4b;
  ASSERT_EQ(f4c.x, 5.0f);
  ASSERT_EQ(f4c.y, 12.0f);
  ASSERT_EQ(f4c.z, 21.0f);
  ASSERT_EQ(f4c.w, 32.0f);

  dkf4 f4c1 = f4a * 11.11f;
  ASSERT_EQ(f4c1.x, 1.f * 11.11f);
  ASSERT_EQ(f4c1.y, 2.f * 11.11f);
  ASSERT_EQ(f4c1.z, 3.f * 11.11f);
  ASSERT_EQ(f4c1.w, 4.f * 11.11f);

  f4a *= f4b;
  ASSERT_EQ(f4a.x, 5.0f);
  ASSERT_EQ(f4a.y, 12.0f);
  ASSERT_EQ(f4a.z, 21.0f);
  ASSERT_EQ(f4a.w, 32.0f);

  f4a *= 20.f;
  ASSERT_EQ(f4a.x, 100.0f);
  ASSERT_EQ(f4a.y, 240.0f);
  ASSERT_EQ(f4a.z, 420.0f);
  ASSERT_EQ(f4a.w, 640.0f);

  dki4 i4a(1, 2, 3, 4);
  dki4 i4b(5, 6, 7, 8);
  dki4 i4c = i4a * i4b;
  ASSERT_EQ(i4c.x, 5);
  ASSERT_EQ(i4c.y, 12);
  ASSERT_EQ(i4c.z, 21);
  ASSERT_EQ(i4c.w, 32);

  dki4 i4c1 = i4a * 1111;
  ASSERT_EQ(i4c1.x, 1111);
  ASSERT_EQ(i4c1.y, 2222);
  ASSERT_EQ(i4c1.z, 3333);
  ASSERT_EQ(i4c1.w, 4444);

  i4a *= i4b;
  ASSERT_EQ(i4a.x, 5);
  ASSERT_EQ(i4a.y, 12);
  ASSERT_EQ(i4a.z, 21);
  ASSERT_EQ(i4a.w, 32);

  i4a *= 20;
  ASSERT_EQ(i4a.x, 100);
  ASSERT_EQ(i4a.y, 240);
  ASSERT_EQ(i4a.z, 420);
  ASSERT_EQ(i4a.w, 640);
}

TEST(Test_dk_math, divide) {
  dkf4 f4a(1.0f, 12.0f, 35.0f, 12.0f);
  dkf4 f4b(5.0f, 6.0f, 7.0f, 8.0f);
  dkf4 f4c = f4a / f4b;
  ASSERT_EQ(f4c.x, 0.2f);
  ASSERT_EQ(f4c.y, 2.f);
  ASSERT_EQ(f4c.z, 5.f);
  ASSERT_EQ(f4c.w, 1.5f);

  dkf4 f4c1 = f4a / 10.f;
  ASSERT_EQ(f4c1.x, 0.1f);
  ASSERT_EQ(f4c1.y, 1.2f);
  ASSERT_EQ(f4c1.z, 3.5f);
  ASSERT_EQ(f4c1.w, 1.2f);
  dkf4 f4c2 = f4a / 10.f / dkf4(10.f);
  ASSERT_EQ(f4c2.x, 1.f / 10.f / 10.f);
  ASSERT_EQ(f4c2.y, 12.f / 10.f / 10.f);
  ASSERT_EQ(f4c2.z, 35.f / 10.f / 10.f);
  ASSERT_EQ(f4c2.w, 12.f / 10.f / 10.f);

  f4a /= f4b;
  ASSERT_EQ(f4a.x, 0.2f);
  ASSERT_EQ(f4a.y, 2.f);
  ASSERT_EQ(f4a.z, 5.f);
  ASSERT_EQ(f4a.w, 1.5f);

  f4a /= 20.f;
  ASSERT_EQ(f4a.x, 0.01f);
  ASSERT_EQ(f4a.y, 0.1f);
  ASSERT_EQ(f4a.z, 0.25f);
  ASSERT_EQ(f4a.w, 0.075f);

  dki4 i4a(1, 12, 35, 12);
  dki4 i4b(5, 6, 7, 8);
  dki4 i4c = i4a / i4b;
  ASSERT_EQ(i4c.x, 0);
  ASSERT_EQ(i4c.y, 2);
  ASSERT_EQ(i4c.z, 5);
  ASSERT_EQ(i4c.w, 1);

  dki4 i4c1 = i4a / 10;
  ASSERT_EQ(i4c1.x, 0);
  ASSERT_EQ(i4c1.y, 1);
  ASSERT_EQ(i4c1.z, 3);
  ASSERT_EQ(i4c1.w, 1);

  i4a /= i4b;
  ASSERT_EQ(i4a.x, 0);
  ASSERT_EQ(i4a.y, 2);
  ASSERT_EQ(i4a.z, 5);
  ASSERT_EQ(i4a.w, 1);

  i4a /= 20;
  ASSERT_EQ(i4a.x, 0);
  ASSERT_EQ(i4a.y, 0);
  ASSERT_EQ(i4a.z, 0);
  ASSERT_EQ(i4a.w, 0);
}

TEST(Test_dk_math, Set0) {
  dkf4 f4a(1.0f, 2.0f, 3.0f, 12.0f);
  f4a.Set0();
  ASSERT_EQ(f4a.x, 0.0);
  ASSERT_EQ(f4a.y, 0.0);
  ASSERT_EQ(f4a.z, 0.0);
  ASSERT_EQ(f4a.w, 0.0);

  dki4 i4a(1, 2, 3, 12);
  i4a.Set0();
  ASSERT_EQ(i4a.x, 0);
  ASSERT_EQ(i4a.y, 0);
  ASSERT_EQ(i4a.z, 0);
  ASSERT_EQ(i4a.w, 0);
}

TEST(Test_dk_math, Set) {
  dkf4 f4a(1.0f, 2.0f, 3.0f, 12.0f);
  f4a.Set(5.f, 6.f, 7.f, 8.f);
  ASSERT_EQ(f4a.x, 5.f);
  ASSERT_EQ(f4a.y, 6.f);
  ASSERT_EQ(f4a.z, 7.f);
  ASSERT_EQ(f4a.w, 8.f);
  f4a.Set(3.f);
  ASSERT_EQ(f4a.x, 3.f);
  ASSERT_EQ(f4a.y, 3.f);
  ASSERT_EQ(f4a.z, 3.f);
  ASSERT_EQ(f4a.w, 3.f);

  dki4 i4a(1, 2, 3, 12);
  i4a.Set(5, 6, 7, 8);
  ASSERT_EQ(i4a.x, 5);
  ASSERT_EQ(i4a.y, 6);
  ASSERT_EQ(i4a.z, 7);
  ASSERT_EQ(i4a.w, 8);
  i4a.Set(3);
  ASSERT_EQ(i4a.x, 3);
  ASSERT_EQ(i4a.y, 3);
  ASSERT_EQ(i4a.z, 3);
  ASSERT_EQ(i4a.w, 3);
}

TEST(Test_dk_math, Add) {
  dkf4 f4a(1.0f, 2.0f, 3.0f, 12.0f);
  dkf4 f4b(5.0f, 6.0f, 7.0f, 8.0f);
  f4a.Add(f4b);
  ASSERT_EQ(f4a.x, 6.f);
  ASSERT_EQ(f4a.y, 8.f);
  ASSERT_EQ(f4a.z, 10.f);
  ASSERT_EQ(f4a.w, 20.f);
  f4a.Add(11.11f);
  ASSERT_EQ(f4a.x, 17.11f);
  ASSERT_EQ(f4a.y, 19.11f);
  ASSERT_EQ(f4a.z, 21.11f);
  ASSERT_EQ(f4a.w, 31.11f);
  f4a.Add(1.f, 2.f, 3.f, 4.f);
  ASSERT_EQ(f4a.x, 18.11f);
  ASSERT_EQ(f4a.y, 21.11f);
  ASSERT_EQ(f4a.z, 24.11f);
  ASSERT_EQ(f4a.w, 35.11f);

  dki4 i4a(1, 2, 3, 12);
  dki4 i4b(5, 6, 7, 8);
  i4a.Add(i4b);
  ASSERT_EQ(i4a.x, 6);
  ASSERT_EQ(i4a.y, 8);
  ASSERT_EQ(i4a.z, 10);
  ASSERT_EQ(i4a.w, 20);
  i4a.Add(1111);
  ASSERT_EQ(i4a.x, 1117);
  ASSERT_EQ(i4a.y, 1119);
  ASSERT_EQ(i4a.z, 1121);
  ASSERT_EQ(i4a.w, 1131);
  i4a.Add(1, 2, 3, 4);
  ASSERT_EQ(i4a.x, 1118);
  ASSERT_EQ(i4a.y, 1121);
  ASSERT_EQ(i4a.z, 1124);
  ASSERT_EQ(i4a.w, 1135);
}

TEST(Test_dk_math, Dot) {
  dkf4 f4a(1.0f, 2.0f, 3.0f, 12.0f);
  dkf4 f4b(5.0f, 6.0f, 7.0f, 8.0f);
  float v = f4a.Dot(f4b);
  ASSERT_EQ(v, 5.f + 12.f + 21.f + 96.f);

  dki4 i4a(1, 2, 3, 12);
  dki4 i4b(5, 6, 7, 8);
  int v2 = i4a.Dot(i4b);
  ASSERT_EQ(v2, 5 + 12 + 21 + 96);
}

TEST(Test_dk_math, MagnitudeSquared) {
  dkf4 f4a(3.0f, 4.0f, 0.0f, 0.0f);
  float v = f4a.MagnitudeSquared();
  ASSERT_EQ(v, 25.f);
  float sv = f4a.Magnitude();
  ASSERT_EQ(sv, 5.f);

  dki4 i4a(3, 4, 0, 0);
  float v2 = i4a.MagnitudeSquared();
  ASSERT_EQ(v2, 25.f);
  float sv2 = i4a.Magnitude();
  ASSERT_EQ(sv2, 5);
}

TEST(Test_dk_math, Normalize) {
  dkf4 f4a(0.0f, 0.0f, 3.0f, 4.0f);
  f4a.Normalize();
  ASSERT_EQ(f4a.x, 0.f / 5.f);
  ASSERT_EQ(f4a.y, 0.f / 5.f);
  ASSERT_EQ(f4a.z, 3.f / 5.f);
  ASSERT_EQ(f4a.w, 4.f / 5.f);

  ASSERT_EQ(f4a.r, 0.f / 5.f);
  ASSERT_EQ(f4a.g, 0.f / 5.f);
  ASSERT_EQ(f4a.b, 3.f / 5.f);
  ASSERT_EQ(f4a.a, 4.f / 5.f);
}

TEST(Test_dk_math, Data) {
  dkf4 f4a(1.0f, 2.0f, 3.0f, 4.0f);
  float* pv = f4a.Data();
  ASSERT_EQ(*pv, 1.f);
  ASSERT_EQ(*++pv, 2.f);
  ASSERT_EQ(*++pv, 3.f);
  ASSERT_EQ(*++pv, 4.f);
}

#endif
